package com.java8.stream;

import com.java8.lambda.bean.Employee;
import org.junit.Test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

/**
 * @author xuehu
 * @date 2020/11/30 13:53
 * Stream API的终止操作
 */

public class StreamEndApiTest {


    List<Employee> employees = Arrays.asList(
            new Employee("张三", 18, 9999.99, Employee.Status.FREE),
            new Employee("李四", 38, 5555.55, Employee.Status.BUSY),
            new Employee("王五", 50, 6666.99, Employee.Status.VOCATION),
            new Employee("赵六", 16, 8888.99, Employee.Status.FREE),
            new Employee("田七", 8, 7777.99, Employee.Status.VOCATION)

    );


    /**
     * 查找与匹配
     * allMatch -- 检查是否匹配所有元素
     * anyMatch -- 检查是否至少匹配一个元素
     * noneMatch -- 检查是否没有匹配所有元素
     * findFirst -- 返回第一个元素
     * findAny -- 返回当前流中的任意元素
     * count -- 返回流中元素的总个数
     * max -- 返回流中最大值
     * min -- 返回流中最小值
     */
    @Test
    public void test1() {
        //检查是否匹配所有元素
        boolean allMatch = employees.stream().allMatch(employee -> employee.getStatus().equals(Employee.Status.BUSY));
        System.out.println(allMatch);

        //检查是否至少匹配一个元素
        boolean anyMatch = employees.stream().anyMatch(e -> e.getStatus().equals(Employee.Status.BUSY));
        System.out.println(anyMatch);

        //检查是否没有匹配所有元素
        boolean noneMatch = employees.stream().noneMatch(e -> e.getStatus().equals(Employee.Status.VOCATION));
        System.out.println(noneMatch);

        //返回第一个元素
        /**
         * java8会尽量避免空指针异常，Optional就是如此诞生的容器类
         */
        Optional<Employee> optionalEmployee = employees.stream()
                .sorted((o1, o2) -> Double.compare(o1.getMoney(), o2.getMoney())).findFirst();

        //如果optionalEmployee对象中的数据为空，就可以使用orElse方法使用一个替代的对象
        optionalEmployee.orElse(new Employee());

        //获取Optional的对象
        System.out.println(optionalEmployee.get());


        //返回当前流中的任意元素
        Optional<Employee> optionalEmployeeFindAny = employees.stream()
                .filter(employee -> employee.getStatus().equals(Employee.Status.FREE))
                .findAny();
        System.out.println(optionalEmployeeFindAny.get());

    }


    @Test
    public void test2() {
        //统计数量
        long count = employees.stream().count();
        System.out.println(count);

        //统计最大值
        Optional<Employee> employee = employees.stream().max((e1, e2) -> {
            return Double.compare(e1.getMoney(), e2.getMoney());
        });

        System.out.println(employee.get());

        //统计最小值
        Optional<Double> aDouble = employees.stream().map(Employee::getMoney).min(Double::compareTo);

        System.out.println(aDouble.get());
    }

    /**
     * 归约
     * reduce(T identity, BinaryOperator) / reduce(BinaryOperator) -- 可以将流中元素反复结合起来,得到一个值
     * reduce方法主要用于对stream中的元素进行聚合求值,最常见的用法是将stream中的一连串的值合成为单个值,比如为一个包含一系列
     * 数值的数组求和。
     * <p>
     * reduce方法有三个重载的方法,方法签名如下:
     * <p>
     * Optional<T> reduce(BinaryOperator<T> accumulator)
     * <p>
     * T reduce(T identity, BinaryOperator<T> accumulator)
     *
     * <U> U reduce(U indntity,BinFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner);
     */

    @Test
    public void test3() {

        //第一个签名方法接受一个BinaryOperator类型的lambda表达式,常规应用方法如下
        /**
         * 代码实现了对numList中的元素的累加,
         * lambada表达式的a参数是表达式的执行结果的缓存，
         * 也就是表达式这一次的执行结果会被作为下一次执行的参数，
         * 而第二个参数b则是依次为stream中每个元素。如果表达式是第一次被执行，a则是stream中的第一个元素。
         *
         */
        List<Integer> numList = Arrays.asList(1, 2, 3, 4, 5);
        Integer integer = numList.stream().reduce((a, b) -> {
            System.out.println("a = " + a + ",b = " + b);
            return a + b;
        }).get();
        System.out.println(integer);


        //第二个签名实现
        /**
         * 与第一个签名的实现唯一的区别是它首次执行时表达式第一次参数并不是stream的第一个元素,
         * 而是通过签名的第一个参数indentity来指定。
         * 我们来通过这个签名对之前的求和代码进行改进
         */
        List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
        //先把0作为x,然后从integerList中取出1,赋值给y,然后将x和y的和赋值给下一个x,然后再从integerList取出一个值赋值给y,依次进行
        Integer reduceSum = integerList.stream().reduce(0, (x, y) -> x + y);
        System.out.println(reduceSum);


        /**
         * 第三种签名的用法相比较前两种稍显复杂，由于前两种实现有一个缺陷,它们的计算结果必须和Stream中的元素类型相同，如上面的代码示例,
         * stream中的类型为int,那么元素结果也必须为int,这也导致灵活性不足,甚至无法完成某些任务,比如我们咬对一个一系列int值求和,但是求和的结果
         * 用一个int类型已经放不下了,升级为long类型,此实第三签名就能发挥价值了,它不执行结果与stream中元素的类型邦死。
         */
        List<Integer> integers = Arrays.asList(Integer.MAX_VALUE, Integer.MAX_VALUE);

        Long reduce = numList.stream().reduce(0L, (a, b) -> a + b, (a, b) -> 0L);
        System.out.println(reduce);

        /**
         * 如上代码所示，它能见int类型的列表合并成long类型的结果。
         * 当然这只是其中一种应用罢了，由于拜托了类型的限制我们还可以通过他来灵活的完成许多任务，
         * 比入将一个int类型的ArrayList转换成一个String类型的ArrayList
         */

        List<Integer> numLists = Arrays.asList(1, 2, 3, 4, 5, 6);
        ArrayList<String> result = numLists.stream().reduce(new ArrayList<String>(), (a, b) -> {
            a.add("element-" + Integer.toString(b));
            return a;
        }, (a, b) -> null);
        System.out.println(result);


    }
}
